//===============================
//  功能 ：备注 
//  作者 ：上中野辅亚瑟王 
//  创建时间 ：2024-10-31  17-37-34 
//  Unity版本  ：2019.4.35f1 
//  变更时间 :  2024-10-31  17-37-34 
//===============================





using UnityEngine;

public class FOVMesh
{

    [Range(0f,50f)]
    public float Length;
    [Range(0f,10f)]
    public float BaseSize;
    [Range(1f, 180f)]
    public float FOVAngle ;
    [Range(1f, 180f)]
    public float ElevationAngle;
    [Range(0, 10)]
    public int Resolution;

    public Mesh Mesh => _mesh;

    private Mesh _mesh;

    private Vector3[] _pts;

    private int[] _triangles;

    private Vector3[] _normals;

    public FOVMesh() 
    {
        _mesh = new Mesh();
    }

    public FOVMesh(float length,float baseSize,float fovAngle, float elevationAngle,int resolution) 
    {
        _mesh = new Mesh();
        UpdateFOVMesh(length, baseSize, fovAngle, elevationAngle, resolution);
    }

    public void UpdateFOVMesh(float length, float baseSize, float fovAngle, float elevationAngle, int resolution)
    {
        Length = Mathf.Clamp(length, 0, 50);
        BaseSize = Mathf.Clamp(baseSize, 0, 10);
        FOVAngle = Mathf.Clamp(fovAngle, 1, 180);
        ElevationAngle = Mathf.Clamp(elevationAngle, 1, 180);
        Resolution = Mathf.Clamp(resolution, 0, 10);
        Init();
    }


    public void Init()
    {
        _pts = new Vector3[4 + (2 + Resolution) * (2 + Resolution)];
        // There are 2 triangles on the base
        int baseTriangleIndices = 2 * 3;
        // The arc is (Resolution+2) vertices to each side, making (Resolution+1)*(Resolution+1) boxes of 2 tris each
        int arcTriangleIndices = (Resolution + 1) * (Resolution + 1) * 2 * 3;
        // There are 4 sides to the cone, and each side has Resolution+2 triangles
        int sideTriangleIndices = (Resolution + 2) * 3;

        _triangles = new int[baseTriangleIndices + arcTriangleIndices + sideTriangleIndices * 4];

        // Base points
        _pts[0] = new Vector3(-BaseSize / 2f, -BaseSize / 2f, 0f); // Bottom Left
        _pts[1] = new Vector3(BaseSize / 2f, -BaseSize / 2f, 0f);  // Bottom Right
        _pts[2] = new Vector3(BaseSize / 2f, BaseSize / 2f, 0f);   // Top Right
        _pts[3] = new Vector3(-BaseSize / 2f, BaseSize / 2f, 0f);  // Top Left

        _triangles[0] = 2; _triangles[1] = 1; _triangles[2] = 0; _triangles[3] = 3; _triangles[4] = 2; _triangles[5] = 0;

        for (int y = 0; y < 2 + Resolution; y++)
        {
            for (int x = 0; x < 2 + Resolution; x++)
            {
                int i = 4 + y * (2 + Resolution) + x;
                float ay = Mathf.Lerp(-FOVAngle / 2f, FOVAngle / 2f, (float)x / (float)(Resolution + 1));
                float ax = Mathf.Lerp(-ElevationAngle / 2f, ElevationAngle / 2f, (float)y / (float)(Resolution + 1));
                Vector3 p = Quaternion.Euler(ax, ay, 0f) * Vector3.forward * Length;
                _pts[i] = p;

                if (x < (1 + Resolution) && y < (1 + Resolution))
                {
                    int ti = baseTriangleIndices + (y * (Resolution + 1) + x) * 3 * 2;
                    _triangles[ti] = i + 1 + (2 + Resolution); // top right
                    _triangles[ti + 1] = i + 1; // bottom right
                    _triangles[ti + 2] = i; // bottom left
                    _triangles[ti + 3] = i + (2 + Resolution); // top left
                    _triangles[ti + 4] = i + (2 + Resolution) + 1; // top right
                    _triangles[ti + 5] = i; // bottom left
                }
            }
        }

        // Top and bottom side triangles
        for (int x = 0; x < 2 + Resolution; x++)
        {
            int iTop = 4 + x;
            int iBottom = 4 + (1 + Resolution) * (2 + Resolution) + x;

            int tiTop = baseTriangleIndices + arcTriangleIndices + x * 3;
            int tiBottom = tiTop + sideTriangleIndices;

            if (x == 0)
            {
                _triangles[tiTop] = 2;
                _triangles[tiTop + 1] = 3;
                _triangles[tiTop + 2] = iTop;

                _triangles[tiBottom] = 0;
                _triangles[tiBottom + 1] = 1;
                _triangles[tiBottom + 2] = iBottom;
            }
            else
            {
                _triangles[tiTop] = iTop;
                _triangles[tiTop + 1] = 2;
                _triangles[tiTop + 2] = iTop - 1;

                _triangles[tiBottom] = 1;
                _triangles[tiBottom + 1] = iBottom;
                _triangles[tiBottom + 2] = iBottom - 1;
            }
        }

        // Left and right side triangles
        int yIncr = 2 + Resolution;
        for (int y = 0; y < 2 + Resolution; y++)
        {
            int iLeft = 4 + y * (2 + Resolution);
            int iRight = iLeft + (1 + Resolution);

            int tiLeft = baseTriangleIndices + arcTriangleIndices + sideTriangleIndices * 2 + y * 3;
            int tiRight = tiLeft + sideTriangleIndices;
            if (y == 0)
            {
                _triangles[tiLeft] = 3;
                _triangles[tiLeft + 1] = 0;
                _triangles[tiLeft + 2] = iLeft;

                _triangles[tiRight] = 1;
                _triangles[tiRight + 1] = 2;
                _triangles[tiRight + 2] = iRight;
            }
            else
            {
                _triangles[tiLeft] = 0;
                _triangles[tiLeft + 1] = iLeft;
                _triangles[tiLeft + 2] = iLeft - yIncr;

                _triangles[tiRight] = iRight;
                _triangles[tiRight + 1] = 1;
                _triangles[tiRight + 2] = iRight - yIncr;
            }
        }


        // 计算法线  
        _normals = new Vector3[_pts.Length];
        for (int i = 0; i < _triangles.Length / 3; i++)
        {
            int i0 = _triangles[i * 3];
            int i1 = _triangles[i * 3 + 1];
            int i2 = _triangles[i * 3 + 2];

            Vector3 v0 = _pts[i0];
            Vector3 v1 = _pts[i1];
            Vector3 v2 = _pts[i2];

            Vector3 normal = Vector3.Cross(v1 - v0, v2 - v0).normalized;

            _normals[i0] += normal;
            _normals[i1] += normal;
            _normals[i2] += normal;
        }

        // 平均化法线  
        for (int i = 0; i < _normals.Length; i++)
        {
            // 这里我们简单地通过除以3来近似平均化，但更好的做法是使用一个计数器来跟踪每个顶点被多少个三角形共享。  
            // 然而，由于我们是在为每个三角形单独添加法线，所以这里除以3是一个合理的近似（假设Mesh是封闭的，或者至少每个顶点都被相同数量的三角形共享）。  
            // 如果Mesh不是封闭的，或者顶点共享的情况不一致，那么这种方法可能会导致某些顶点的法线不准确。  
            _normals[i] = _normals[i].normalized; // 确保法线是单位向量  
        }



         _mesh.vertices = _pts;
        _mesh.triangles = _triangles;
        _mesh.normals = _normals;
        _mesh.name = "FOV Mesh";

    }

    public void ShowGizmos(Transform transform) 
    {
        Gizmos.color = Color.green;
        foreach (Vector3 p in _pts)
        {
            Gizmos.DrawSphere(transform.TransformPoint(p), 0.1f);
        }
        Gizmos.DrawWireMesh(Mesh,0,transform.position,transform.rotation,transform.localScale);
    }

}
